package com.breaktian.assemble.customviews.loading;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.LinearInterpolator;

import com.breaktian.assemble.R;
import com.breaktian.assemble.utils.DimensConversion;


/**
 * data: 2016/12/22
 * func: 支付时的loading 动画
 * desc: 布局设置需要为同样的宽高,内部代码不做限制,内部的部分逻辑处理按照正方形处理,若是采用长方形会出现异常
 * 待确定最终的Dialog方案时,进行进一步的封装
 */

public class LoadingView extends View {

    /**
     * 支付Loading模式，动画停止时只转圈
     */
    private static final int MODE_LOADING = 0x0001;
    /**
     * 支付Loading模式，动画停止时会打勾
     */
    private static final int MODE_LOADING_SUCCESS = 0x0002;
    /**
     * 支付Loading模式，动画停止时会打错
     */
    private static final int MODE_LOADING_FAILURE = 0x0004;

    /* Loading模式 */
    private int mLoadingMode;

    protected Paint outSideCirclePaint;
    protected Paint innerCirclePaint;
    protected Paint rightLineFirPaint;
    protected int drawStatues;
    protected final int DrawStatuesBegin = 0x00;
    protected final int DrawStatuesInnerCircle = 0x01;
    protected final int DrawStatuesRightFir = 0x03;
    protected final int DrawStatuesRightSec = 0x04;
    protected final int DrawStatuesWrong1 = 0x08;
    protected final int DrawStatuesWrong2 = 0x10;

    protected ValueAnimator innerCircleAnimator;
    protected int totalWidth;
    protected int totalHeight;
    protected float outSideRadius;
    protected float innerSideRadius;
    protected float outSideStokeWidth = 6;
    protected float innerSideStockWidth = 2;
    protected float rightLineStockWidth = 7;
    protected float innerCircleLastX, innerCircleLastY, rightLastX, rightLastY;
    protected float rightStartX, rightStartY, rightMiddleX, rightMiddleY, rightEndX, rightEndY;

    private Path wrongPath1 = new Path(), wrongPath2 = new Path();
    private Path animPath = new Path();
    private PathMeasure mWrongMeasure = new PathMeasure();

    private Path rightFirPath, rightSecPath;
    private Paint rightLineSecPaint;

    public LoadingView(Context context) {
        this(context, null);
        init();
    }

    public LoadingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs);
        init();
    }

    /**
     * 开始Loading
     */
    public void start() {
        if (!isLoading()) {
            drawStatues = DrawStatuesBegin;
            invalidate();
        }
    }

    /**
     * 停止Loading，并打勾
     */
    public void stopTick() {
        stop(MODE_LOADING_SUCCESS);
    }

    /**
     * 停止Loading，并打勾
     */
    public void stopWrong() {
        stop(MODE_LOADING_FAILURE);
    }

    /**
     * 停止Loading
     */
    public void stopNormal() {
        stop(MODE_LOADING);
    }

    /**
     * 判断是否在Loading
     *
     * @return true，loading状态；false，loading停止状态
     */
    public boolean isLoading() {
        return innerCircleAnimator != null && innerCircleAnimator.isRunning();
    }

    /**
     * 停止Loading
     *
     */
    private void stop(int mode) {
        if (isLoading()) {
            mLoadingMode = mode;
            innerCircleAnimator.setRepeatCount(0);
        }
    }

    private void init() {
        mLoadingMode = MODE_LOADING;

        this.setBackgroundColor(Color.WHITE);
        int outSideCircleColor = Color.parseColor("#ffd8e6");
        int innerCircleColor = Color.parseColor("#ff3e83");
        int rightLineFirColor = innerCircleColor;
        int rightLineSecColor = Color.parseColor("#ff86b2");

        outSideCirclePaint = new Paint();
        outSideCirclePaint.setStrokeWidth(outSideStokeWidth);
        outSideCirclePaint.setColor(outSideCircleColor);
        outSideCirclePaint.setAntiAlias(true);
        outSideCirclePaint.setStyle(Paint.Style.STROKE);

        innerCirclePaint = new Paint();
        innerCirclePaint.setStrokeWidth(innerSideStockWidth);
        innerCirclePaint.setColor(innerCircleColor);
        innerCirclePaint.setAntiAlias(true);
        innerCirclePaint.setStyle(Paint.Style.FILL);

        rightLineFirPaint = new Paint();
        rightLineFirPaint.setStrokeWidth(rightLineStockWidth);
        rightLineFirPaint.setColor(rightLineFirColor);
        rightLineFirPaint.setAntiAlias(true);
        rightLineFirPaint.setStyle(Paint.Style.STROKE);
        rightLineFirPaint.setDither(true);
        rightLineFirPaint.setStrokeCap(Paint.Cap.ROUND);


        rightLineSecPaint = new Paint(rightLineFirPaint);
        rightLineSecPaint.setColor(rightLineSecColor);
        drawStatues = -1;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawOutSideCircle(canvas);
        switch (drawStatues) {
            case DrawStatuesBegin:
                initDrawInnerCircle();
                break;
            case DrawStatuesInnerCircle:
                drawInnerCircle(canvas);
                break;
            case DrawStatuesRightFir:
                drawRightLineFir(canvas);
                break;
            case DrawStatuesRightSec:
                drawRightLineSec(canvas);
                break;
            case DrawStatuesWrong1:
                canvas.drawPath(animPath, rightLineSecPaint);
                break;
            case DrawStatuesWrong2:
                canvas.drawPath(wrongPath1, rightLineSecPaint);
                canvas.drawPath(animPath, rightLineSecPaint);
                break;
            default:
                break;
        }
    }

    /**
     * 设置view的宽高
     * <p>
     * 若是xml中设置的对应的宽高不是具体的值,或者宽高不同的情况下，将采用默认的尺寸
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (MeasureSpec.EXACTLY != widthMode || MeasureSpec.EXACTLY != heightMode
                || widthSize != heightSize || widthSize <= 0 || heightSize <= 0) {
            int defaultSize = DimensConversion.dpToPixel(this.getContext(),
                    R.dimen.bm_loading_ico_size);
            widthSize = defaultSize;
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(defaultSize, MeasureSpec.EXACTLY);
            heightMeasureSpec = widthMeasureSpec;
        }
        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
        initMeasureParameter(widthSize, widthSize);
    }

    /**
     * 实际起控制作用的是rightFirLineLength 与 rightSecLineLength
     * 代码已经控制对号的夹角始终为90°  同时rightFirLineLength 与中心线的夹角控制为45°
     *
     * @param width
     * @param height
     */
    private void initMeasureParameter(int width, int height) {
        totalWidth = width;
        totalHeight = height;
        outSideRadius = totalWidth / 2 - outSideStokeWidth / 2;
        innerSideRadius = totalWidth * 0.2f;

        float rightFirLineLength = totalWidth * 0.19f;
        float rightSecLineLength = totalWidth * 0.196f;

        rightStartX = totalWidth / 2 + (float) Math.cos(Math.toRadians(180)) * innerSideRadius;
        rightStartY = totalHeight / 2 + (float) Math.sin(Math.toRadians(180)) * innerSideRadius;

        float tempValue = (float) Math.sqrt(Math.pow(rightFirLineLength, 2) / 2);
        rightMiddleX = rightStartX + tempValue;
        rightMiddleY = rightStartY + tempValue;
        float tempThirdLength = (float) Math.sqrt(Math.pow(rightFirLineLength, 2) +
                Math.pow(rightSecLineLength, 2));
        double tempDegrees = Math.toDegrees(Math.atan(rightSecLineLength / rightFirLineLength)) - 45;

        rightEndX = (float) (rightStartX + tempThirdLength * Math.cos(Math.toRadians(tempDegrees)));
        rightEndY = (float) (rightStartY - tempThirdLength * Math.sin(Math.toRadians(tempDegrees)));

        float r = totalWidth / 2;
        float startX = (float) (r - r / 2 * Math.cos(Math.PI / 4));
        float startY = startX;
        float endX = (float) (r + r / 2 * Math.cos(Math.PI / 4));
        float endY = endX;

        wrongPath1.reset();
        wrongPath1.moveTo(startX, startY);
        wrongPath1.lineTo(endX, endY);

        startX = (float) (r + r / 2 * Math.cos(Math.PI / 4));
        startY = (float) (r - r / 2 * Math.cos(Math.PI / 4));
        endX = startY;
        endY = startX;
        wrongPath2.reset();
        wrongPath2.moveTo(startX, startY);
        wrongPath2.lineTo(endX, endY);
    }

    private void drawRightLineFir(Canvas canvas) {
        if (null == rightFirPath) {
            rightFirPath = new Path();
            rightFirPath.moveTo(rightStartX, rightStartY);
        }
        rightFirPath.lineTo(rightLastX, rightLastY);
        canvas.drawPath(rightFirPath, rightLineFirPaint);
    }

    private void drawRightLineSec(Canvas canvas) {
        if (null == rightSecPath) {
            rightSecPath = new Path();
            rightSecPath.moveTo(rightMiddleX, rightMiddleY);
        }
        rightSecPath.lineTo(rightLastX, rightLastY);
        canvas.drawPath(rightSecPath, rightLineSecPaint);
        canvas.drawLine(rightStartX, rightStartY, rightMiddleX, rightMiddleY, rightLineFirPaint);
    }

    private void drawInnerCircle(Canvas canvas) {
        if (0 == innerCircleLastY || 0 == innerCircleLastY)
            return;
        canvas.drawCircle(innerCircleLastX, innerCircleLastY, 6, innerCirclePaint);
    }

    private void initDrawInnerCircle() {
        innerCircleAnimator = new ValueAnimator().ofFloat(1, 0);
        innerCircleAnimator.setRepeatMode(ValueAnimator.RESTART);
        innerCircleAnimator.setRepeatCount(ValueAnimator.INFINITE);
        innerCircleAnimator.setDuration(700);
        innerCircleAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        drawStatues = DrawStatuesInnerCircle;
        innerCircleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float curValue = (float) animation.getAnimatedValue();
                float curAngle = curValue * 360 + 180;
                float x = (float) Math.cos(Math.toRadians(curAngle)) * innerSideRadius;
                float y = (float) Math.sin(Math.toRadians(curAngle)) * innerSideRadius;
                innerCircleLastX = totalWidth / 2 + x;
                innerCircleLastY = totalHeight / 2 + y;
                postInvalidate();
            }
        });
        innerCircleAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (mLoadingMode == MODE_LOADING_SUCCESS) {
                    initDrawRightLine();
                } else if (mLoadingMode == MODE_LOADING_FAILURE) {
                    drawWrong1();
                }
            }
        });
        innerCircleAnimator.start();
    }

    private void initDrawRightLine() {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setDuration(300);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float lastLineValue = (float) animation.getAnimatedValue();
                rightLastX = rightStartX + (rightMiddleX - rightStartX) * lastLineValue;
                rightLastY = rightStartY + (rightMiddleY - rightStartY) * lastLineValue;
                postInvalidate();
            }
        });

        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                initCompleteRightSecond();
            }

            @Override
            public void onAnimationStart(Animator animation) {
                drawStatues = DrawStatuesRightFir;
            }
        });
        valueAnimator.start();
    }

    private void initCompleteRightSecond() {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setDuration(400);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float lastLineValue = (float) animation.getAnimatedValue();
                rightLastX = rightEndX + (rightEndX - rightMiddleX) * lastLineValue;
                rightLastY = rightEndY - (rightMiddleY - rightEndY) * lastLineValue;
                postInvalidate();
            }
        });

        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                drawStatues = DrawStatuesRightSec;
            }
        });
        valueAnimator.start();
    }

    private void drawWrong1() {
        drawStatues = DrawStatuesWrong1;
        mWrongMeasure.setPath(wrongPath1, false);
        ValueAnimator animator = ValueAnimator.ofFloat(0, mWrongMeasure.getLength());
        animator.setInterpolator(new LinearInterpolator());
        animator.setDuration(300);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float length = (float) animation.getAnimatedValue();
                animPath.reset();
                mWrongMeasure.getSegment(0, length, animPath, true);
                postInvalidate();
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                drawWrong2();
            }
        });
        animator.start();
    }

    private void drawWrong2() {
        drawStatues = DrawStatuesWrong2;
        mWrongMeasure.setPath(wrongPath2, false);
        ValueAnimator animator = ValueAnimator.ofFloat(0, mWrongMeasure.getLength());
        animator.setInterpolator(new LinearInterpolator());
        animator.setDuration(300);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float length = (float) animation.getAnimatedValue();
                animPath.reset();
                mWrongMeasure.getSegment(0, length, animPath, true);
                postInvalidate();
            }
        });
        animator.start();
    }

    protected void drawOutSideCircle(Canvas canvas) {
        canvas.drawCircle(totalWidth / 2, totalHeight / 2, outSideRadius, outSideCirclePaint);
    }


    //just fot test
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (null != innerCircleAnimator && innerCircleAnimator.isRunning()) {
            innerCircleAnimator.cancel();
        }
    }
}
